
#include <QThread>
#include <QProcess>
#include <QDebug>
#include <QFileDialog>
#include <QFileInfo>

#include "mainwindow.h"
#include "./ui_mainwindow.h"

#define SAVE_DIR    "/data/iso/rootfs/kylin-arm64"

namespace TaskType {
enum Type {
    MountISO = 0, UnMountISO, ExtractISO ,
    FoundSquashfs, FoundSquashfsEnd, MountSquashfs, UnMountSquashfs, UnSquashfs,
    PatchAptSandbox, UpdateSourcesList,
    RemoveSavedDir,
    RegisterNSPawn,
    StartTerminalNSPawn, StartTerminalBWrap, StartTerminalDocker, StartTerminal,
    Invalid = -1
};
};

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    tmp = nullptr;

    iso_file = "/data/usershare/iso/Kylin-Desktop-V10-SP1-General-RC4-2403-update2-Build01-20241011-arm64.iso";
    save_dir = "/data/usershare/rootfs/kylin-arm64";

    ui->setupUi(this);

    ui->lineEdit_isofile->setText(iso_file);
    ui->lineEdit_savedir->setText(save_dir);

    connect(ui->pushButton_iso, &QPushButton::clicked, this, &MainWindow::OnPushButtonISOClicked);
    connect(ui->pushButton_save, &QPushButton::clicked, this, &MainWindow::OnPushButtonSaveClicked);
    connect(ui->pushButton_run, &QPushButton::clicked, this, &MainWindow::OnPushButtonRunClicked);
    connect(ui->lineEdit_isofile, &QLineEdit::editingFinished, this, &MainWindow::OnLineEditISOEditingFinished);
    connect(ui->lineEdit_savedir, &QLineEdit::editingFinished, this, &MainWindow::OnLineEditSaveDirEditingFinished);

    mgr = new TaskManager(this);
    connect(mgr, &TaskManager::TaskQueueAllDone, this, &MainWindow::OnTaskQueueAllDone);
    connect(mgr, &TaskManager::TaskFinished, this, &MainWindow::OnTaskFinished);
    connect(mgr, &TaskManager::TaskError, this, &MainWindow::OnTaskError);
    connect(mgr, &TaskManager::TaskOutput, this, &MainWindow::OnTaskOutput);

    iso_mount = CreateTempDir();
}

MainWindow::~MainWindow()
{
    delete ui;
    if (tmp) {
        tmp->remove();
        delete tmp;
    }
}

QString MainWindow::CreateTempDir()
{
    if (!tmp) {
        tmp = new QTemporaryDir();
    } else {
        tmp->remove();
        delete tmp;
        tmp = nullptr;
    }
    if (!tmp->isValid()) {
        qCritical().noquote() << "Create temporary dir failed!" << tmp->errorString();
        return QString();
    }
    return tmp->path();
}

void MainWindow::MountISO()
{
    TaskData task(TaskType::MountISO);
    task.name = QObject::tr("Mount ISO file");
    task.exec = QLatin1String("fuseiso");
    task.args.append(iso_file);
    task.args.append(iso_mount);

    mgr->AttachTaskQueue(task);
}

void MainWindow::UnMountISO()
{
    TaskData task(TaskType::UnMountISO);
    task.name = QObject::tr("UnMount ISO file");
    task.exec = QLatin1String("fusermount");
    task.args.append(QLatin1String("-u"));
    task.args.append(iso_mount);

    mgr->AttachTaskQueue(task);
}

void MainWindow::UnSquashfs()
{
    QSharedPointer<TaskData> error_task = QSharedPointer<TaskData>(new TaskData(TaskType::UnMountISO));
    error_task->name = QObject::tr("UnMount ISO file");
    error_task->exec = QLatin1String("fusermount");
    error_task->args.append(QLatin1String("-u"));
    error_task->args.append(iso_mount);

    TaskData task(TaskType::UnSquashfs);
    task.name = "Unsquash Task";
    task.exec = "fakeroot";
    task.args << "unsquashfs";
    task.args << "-quiet";
    task.args << "-force";
    task.args << "-no-xattrs";
    task.args << "-ignore-errors";
    task.args << "-dest";
    task.args << save_dir;
    task.args << QString("%1/casper/filesystem.squashfs").arg(iso_mount);
    task.error_task = error_task;

    mgr->AttachTaskQueue(task);
}

void MainWindow::OnPushButtonISOClicked()
{
    QString iso = QFileDialog::getOpenFileName(this, tr("Select a ISO file"), QDir::homePath(), tr("ISO Files (*.iso)"), nullptr, QFileDialog::DontResolveSymlinks|QFileDialog::DontUseNativeDialog);
    if (!iso.isEmpty()) {
        ui->lineEdit_isofile->setText(iso);
    }
    OnLineEditISOEditingFinished();
}

void MainWindow::OnPushButtonSaveClicked()
{
    QString default_dir = SAVE_DIR;
    if (QFileInfo::exists(default_dir)) {
        default_dir = QDir::homePath();
    }
    QString dir = QFileDialog::getExistingDirectory(this, tr("Select Save Directory"), default_dir, QFileDialog::DontResolveSymlinks|QFileDialog::ShowDirsOnly|QFileDialog::DontUseNativeDialog);
    if (!dir.isEmpty()) {
        ui->lineEdit_savedir->setText(dir);
    }
    OnLineEditSaveDirEditingFinished();
}

void MainWindow::OnPushButtonRunClicked()
{
    if (!iso_file.isEmpty() && !save_dir.isEmpty()) {
        MountISO();
        UnSquashfs();
        UnMountISO();
        mgr->StartTaskQueue();
    }
}

void MainWindow::OnLineEditISOEditingFinished()
{
    QString iso = ui->lineEdit_isofile->text();
    if (!iso.isEmpty()) {
        iso_file = iso;
        qDebug().noquote() << "Mount ISO:" << iso_file;
    }
}

void MainWindow::OnLineEditSaveDirEditingFinished()
{
    QString dir = ui->lineEdit_savedir->text();
    if (!dir.isEmpty()) {
        save_dir = dir;
        qDebug().noquote() << "Save DIR:" << save_dir;
    }
}

void MainWindow::OnTaskQueueAllDone()
{
    qDebug().noquote() << __FUNCTION__ << "All Done";
}

void MainWindow::OnTaskFinished(const TaskData &data, int exit, const QString &errstr)
{
    qDebug().noquote() << __FUNCTION__ << "Data:" << data.name << ", Exit:" << exit << ", ErrStr:" << errstr;
    if (!errstr.isEmpty()) {
        ui->textBrowser->append(QString("Task %1 finished with %2, %3").arg(data.name, QString::number(exit), errstr));
    } else {
        ui->textBrowser->append(QString("Task %1 finished with %2").arg(data.name, QString::number(exit)));
    }
}

void MainWindow::OnTaskError(const TaskData &data, const QString &errstr)
{
    QStringList lines = errstr.split("\n");
    for (const QString &line : qAsConst(lines)) {
        if (line.isEmpty()) { continue; }
        qDebug().noquote() << __FUNCTION__ << "Data:" << data.name << ", ErrStr:" << line;
        ui->textBrowser->append(QString("Task %1 Error: %2").arg(data.name, line));
    }
}

void MainWindow::OnTaskOutput(const TaskData &data, const QString &outstr)
{
    QStringList lines = outstr.split("\n");
    for (const QString &line : qAsConst(lines)) {
        if (line.isEmpty()) { continue; }
        qDebug().noquote() << __FUNCTION__ << "Data:" << data.name << ", OutStr:" << line;
        ui->textBrowser->append(QString("Task %1 Output: %2").arg(data.name, line));
    }
}

